// Includes
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <time.h>

//Incluyo la clase Mersenne-Twister para la generación de números aleatorios en C++
#include "utils/mt.h"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "utils/Random.cu"

//Incluyo la clase cuPrintf para poder imprimir desde GPU
#include "utils/cuPrintf.cu"

//Incluyo la clase parser.cpp para obetener los parametros
#include "utils/Parser.h"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "utils/global.cuh"

//Incluyo las clases binaria, entera, permutacion y real, las cuales tienen un hilo cada 2 individuos y un bloque por poblacion
#include "representaciones/binaria.cu"
#include "representaciones/entera.cu"
#include "representaciones/permutacion.cu"
#include "representaciones/real.cu"

//Incluyo las clases binariaGen, enteraGen y realGen, las cuales tienen un hilo por gen y un bloque cada 2 individuos
#include "representaciones/binariaGen.cu"
#include "representaciones/enteraGen.cu"
#include "representaciones/realGen.cu"

using namespace std;

//////////////////////
// Program main
//////////////////////

int main( int argc, char** argv){
	clock_t inicio = clock();
	Parser parser = Parser("config.cfg");
	bool esValido = parser.isValidFile();
	if (esValido) {
		cudaPrintfInit();
		float * parametros = asignarParametros(parser);
		dim3 tamGridAsig(1,1); //Grid dimensión
		dim3 tamBlockAsig(1,1,1); //Block dimensión
		int tamVectorParametros = sizeof(float)*cantidadParametros;
		float *parametrosDevice;
		cudaMalloc(&parametrosDevice, tamVectorParametros);
		cudaMemcpy(parametrosDevice, parametros, tamVectorParametros, cudaMemcpyHostToDevice);
		//asignacion de parametros en GPU
		asignarParametrosDevice<<<tamGridAsig, tamBlockAsig>>>(parametrosDevice);
		totalGenesGPU = cantidadPoblacionesGPU * cantCromosomasPoblacion * cantGenesEnCromosoma;
		totalGenesCPU = cantidadPoblacionesCPU * cantCromosomasPoblacion * cantGenesEnCromosoma;
		totalIndividuosGPU = cantCromosomasPoblacion*cantidadPoblacionesGPU;
		totalIndividuosCPU = cantCromosomasPoblacion*cantidadPoblacionesCPU;
		if(tipoRepresentacion == 0 && bloquePorPoblacion == 1){
			representacionBinaria();
		}else if(tipoRepresentacion == 0){
			representacionBinariaGen();
		}else if(tipoRepresentacion == 1 && bloquePorPoblacion == 1){
			representacionEntera();
		}else if(tipoRepresentacion == 1){
			representacionEnteraGen();
		}else if(tipoRepresentacion == 2){
			representacionPermutacion();
		}else if(tipoRepresentacion == 3 && bloquePorPoblacion == 1){
			representacionReal();
		}else{
			representacionRealGen();
		}
		//Se libera la memoria de la GPU
		free(parametros);
		cudaFree(parametrosDevice);
		cudaPrintfDisplay(stdout, true);
		cudaPrintfEnd();
	}
	printf("Tiempo de ejecucion total: %f s\n", ((double)clock() - inicio) / CLOCKS_PER_SEC);
}
